package com.trump.baiduim.netty.server;

import com.alibaba.fastjson.JSONObject;
import com.trump.baiduim.model.Message;
import com.trump.baiduim.model.MessageVO;
import com.trump.baiduim.netty.config.MsgActionEnum;
import com.trump.baiduim.netty.config.SpringContextHolder;
import com.trump.baiduim.netty.config.UserChannel;
import com.trump.baiduim.service.ImService;
import com.trump.baiduim.utils.EncryptUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;

import java.sql.Timestamp;
import java.util.List;
import java.util.Queue;

/**
 * @author guoyuans
 */
@Slf4j
public class WebSocketHandler extends SimpleChannelInboundHandler {
    /**
     * 客户端组
     * 用于记录和管理客户端的channel
     */
    public static ChannelGroup channelGroup;

    /**
     * 记录消息，分为推送成功和离线消息
     */
    ImService imService = SpringContextHolder.getBean(ImService.class);

    /**
     * 消息加密解密工具
     */
    EncryptUtil encryptUtil = SpringContextHolder.getBean(EncryptUtil.class);

    static {
        channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    }

    /**
     * 接受客户端传递的消息
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        Channel currentChannel = ctx.channel();

        /**
         * 文本消息
         */
        if (msg instanceof TextWebSocketFrame) {
            String message = ((TextWebSocketFrame)msg).text();
            log.info("收到客户端消息...");
            /**
             * json消息转化为bean对象,并对消息内容加密
             */
            MessageVO messageVO = null;
            try {
                messageVO = JSONObject.parseObject(message,MessageVO.class);
                messageVO.setCreatedTime(new Timestamp(System.currentTimeMillis()));
                messageVO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
            } catch (Exception e) {
                log.error("json解析出错，请传递json报文",e);
                return;
            }
            /**
             * 得到执行的动作
             */
            Integer action = messageVO.getAction();
            /**
             * 客户端第一次连接或者重新连接时
             */
            if (action.equals(MsgActionEnum.CONNECT.type)) {
                String messageSender = messageVO.getMessageSender();
                UserChannel.put(messageSender,currentChannel);

                /**
                 * 离线消息推送,并对消息状态与更新时间进行更新
                 */
                Queue<Message> tmpMessage = imService.queryNoSendMessage(messageSender);
                while (!tmpMessage.isEmpty()) {
                    imService.updateMessageStatus(tmpMessage.peek().getMessageId(),new Timestamp(System.currentTimeMillis()));
                    sendMessage(messageSender,tmpMessage.poll().getMessageContent());
                }
            } else if (action.equals(MsgActionEnum.CHAT.type)) {
                /**
                 * 聊天类型消息，保存聊天记录并记录消息的签收状态
                 */
                String messageReceiver = messageVO.getMessageReceiver();
                messageVO.setMessageContent(encryptUtil.encryptMessage(messageVO.getMessageContent(),
                        imService.findUserByAccount(messageVO.getMessageReceiver()).getUserSalt()));
                /**
                 * 发送消息给指定的用户
                 */
//                if (Chat验证消息类型)
                
                if (messageReceiver != null && UserChannel.isContainKey(messageReceiver)) {

                    sendMessage(messageReceiver,messageVO.getMessageContent());

                    /**
                     * 消息状态
                     */
                    messageVO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
                    messageVO.setMessageStatus("已签收");
                    imService.insertMessage(messageVO);
                } else {
                    log.info("消息接受者离线");
                    messageVO.setMessageStatus("未签收");
                    imService.insertMessage(messageVO);
                }
            }
            
            if (msg instanceof BinaryWebSocketFrame) {
                log.info("收到二进制消息: "+((BinaryWebSocketFrame)msg).content().readableBytes());
//                BinaryWebSocketFrame binaryWebSocketFrame = new BinaryWebSocketFrame(Unpooled.buffer().writeBytes(
//                        (((BinaryWebSocketFrame)msg).content()).toString().getBytes()));
//                ctx.channel().writeAndFlush(binaryWebSocketFrame);
                String messageReceiver = messageVO.getMessageReceiver();
                if (messageReceiver != null && UserChannel.isContainKey(messageReceiver)) {
//                    sendMessage(messageReceiver,JSONObject.toJSONString(messageVO));
                    sendBinaryFile(messageReceiver,messageVO.getMessageContent());
                    /**
                     * 消息状态
                     */
                    messageVO.setMessageStatus("已签收");
                    imService.insertMessage(messageVO);
                } else {
                    log.info("消息接受者离线");
                    messageVO.setMessageStatus("未签收");
                    imService.insertMessage(messageVO);
                }
                sendBinaryFile(messageReceiver,msg);
            }
            
            if (msg instanceof CloseWebSocketFrame) {
                log.info("客户端关闭");
                Channel channel = ctx.channel();
                channel.close();
            }
            
        }
    }

    @Override
    public void handlerAdded ( ChannelHandlerContext ctx ) throws Exception {
        System.out.println("与客户端连接成功");
        channelGroup.add(ctx.channel());
    }
    
    @Override
    public void handlerRemoved ( ChannelHandlerContext ctx ) throws Exception {
        /**
         * 当触发handlerRemoved,ChannelGroup会自动移除对应的客户端的channel
         */
        log.info("客户端断开，channel对应的长id为:" + ctx.channel().id().asLongText());
        log.info("客户端断开，channel对应的短id为:" + ctx.channel().id().asShortText());
    }
    
    
    
    public void sendMessage(String messageReceiver,String message) {
        Channel channel = UserChannel.get(messageReceiver);
        String salt = imService.findUserByAccount(messageReceiver).getUserSalt();
        channel.writeAndFlush(new TextWebSocketFrame(encryptUtil.decryptMessage(message,salt)));
    }
    
    public void sendBinaryFile(String messageReceiver,Object message) {
        Channel channel = UserChannel.get(messageReceiver);
        BinaryWebSocketFrame binaryWebSocketFrame = new BinaryWebSocketFrame(Unpooled.buffer().writeBytes(
                (((BinaryWebSocketFrame)message).content()).toString().getBytes()));
        channel.writeAndFlush(binaryWebSocketFrame);
    }
    
    public void sendMessageAll(String messsage) {
        channelGroup.writeAndFlush(new TextWebSocketFrame(messsage));
    }
}
